package nfes.the.util;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.mapper.CannotResolveClassException;
import com.thoughtworks.xstream.mapper.MapperWrapper;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.UnrecoverableEntryException;
import java.text.Normalizer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Scanner;
import javax.security.cert.CertificateException;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.DigestMethod;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.SignedInfo;
import javax.xml.crypto.dsig.Transform;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import nfes.the.classes.XmlHelper;

import org.apache.axis.encoding.Base64;
import org.w3c.dom.Element;

/**
 * @author Julianna Paz
 */
public class Util {

    public static Date data = new Date();
//    public static SimpleDateFormat dt = new SimpleDateFormat("ddMMyyyyhms");
    public static SimpleDateFormat dt = new SimpleDateFormat("ddMMyyyy");
    public static String hoje = dt.format(data);
    private static String senhaCertificado = "certificado";
    private static String caminhoCertificado = Constantes.caminhoCertificado;

    /**
     * Gerar arquivo xml de acordo com os dados enviados do webservice
     *
     * @param caminhoArquivo
     * @param nomeArquivo
     * @param tipo
     * @param dadosParaGravar
     * @author Julianna Paz
     */
    public static void gerarArquivo(String caminhoArquivo, String nomeArquivo, String tipo, String dadosParaGravar) {
        try {
            FileWriter arq = new FileWriter(caminhoArquivo + nomeArquivo + "." + tipo);
            PrintWriter gravarArq = new PrintWriter(arq);
            gravarArq.println(dadosParaGravar);
            arq.close();
        } catch (IOException e) {
            System.err.printf("Erro na gravacao do arquivo: %s.\n", e.getMessage());
        }
    }

    public static String dataHoje() {
        return hoje;
    }

    /**
     * Informar caminho do arquivo xml para que ele possa ser lido e o
     * convertido em texto
     *
     * @param String
     * @return String
     * @author Julianna Paz
     */
    public static String lerArquivo(String caminhoArquivo) {
        Scanner ler = new Scanner(caminhoArquivo);
        String nome = ler.nextLine();
        StringBuilder xml = new StringBuilder();

        try {
            FileReader arq = new FileReader(nome);
            BufferedReader lerArq = new BufferedReader(arq);
            // lê a primeira linha a variável "linha" recebe o valor "null" quando o processo de repetição atingir o final do arquivo texto
            String linha = lerArq.readLine();

            while (linha != null) {
                xml = xml.append(linha);
                linha = lerArq.readLine();
            }

            arq.close();
        } catch (IOException e) {
            System.err.printf("Erro na abertura do arquivo: %s.\n", e.getMessage());
        }

        return xml.toString();
    }

    public static void setProxy(String host, String porta, String user, String senha) {
        System.setProperty("http.proxyHost", host);
        System.setProperty("http.proxyPort", porta);
        System.setProperty("http.proxyUser", user);
        System.setProperty("http.proxyPassword", senha);
    }

    public static String sha1(String input) throws NoSuchAlgorithmException {
        MessageDigest mDigest = MessageDigest.getInstance("SHA1");
        byte[] result = mDigest.digest(input.getBytes());
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < result.length; i++) {
            sb.append(Integer.toString((result[i] & 0xff) + 0x100, 16).substring(1));
        }

        return sb.toString();
    }

    @Deprecated
    public static String gerarAssinatura(String valor) {
        try {

            Signature dsa = Signature.getInstance("SHA1withRSA");

            dsa.initSign(getPrivateKey());
            dsa.update(valor.getBytes());

            return new String(Base64.encode(dsa.sign()));

        } catch (Exception ex) {

            ex.printStackTrace();

        }
        return "";
    }

    @Deprecated
    public static PrivateKey getPrivateKey() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableEntryException, java.security.cert.CertificateException {

        KeyStore ks = KeyStore.getInstance("PKCS12");

        ks.load(new FileInputStream(caminhoCertificado), senhaCertificado.toCharArray());

        String alias = (String) ks.aliases().nextElement();

        KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) ks.getEntry(alias, new KeyStore.PasswordProtection(senhaCertificado.toCharArray()));

        return keyEntry.getPrivateKey();
    }

    /**
     * Método utilizado para gerar a chave para conversão em sha1 de acordo com
     * a posição e parametros passados
     *
     * @author Julianna Paz
     * @param inscricaoMunicipalPrestador
     * @param serieRps
     * @param numeroRps
     * @param dataEmissaoRPS
     * @param tributacao
     * @param situacaoRps
     * @param tipoRecolhimento
     * @param valorServico
     * @param valorDeducao
     * @param codigoAtividade
     * @param cpfCnpjTomador
     */
    public static String montarTextoParaAssinatura(String inscricaoMunicipalPrestador, String serieRps,
            Integer numeroRps, String dataEmissaoRPS, String tributacao, String situacaoRps,
            String tipoRecolhimento, Integer valorServico, Integer valorDeducao, String codigoAtividade,
            String cpfCnpjTomador) {

        StringBuilder chave = new StringBuilder();

        //Tamanho 11, Preencher com zeros a esquerda
        if (inscricaoMunicipalPrestador.length() == 11) {
            chave.append(inscricaoMunicipalPrestador);
        } else if (inscricaoMunicipalPrestador.length() > 11) {
            //Lançar exceção
        } else {
            int cont = 11 - inscricaoMunicipalPrestador.length();
            for (int i = 0; i < cont; i++) {
                chave.append("0");
            }
            chave.append(inscricaoMunicipalPrestador);
        }

        //Tamanho 5, Preencher com espaços em branco a direita
        if (serieRps.length() == 5) {
            chave.append(serieRps);
        } else if (serieRps.length() > 5) {
            //lança excessão
        } else {
            chave.append(serieRps);
            int cont = 5 - serieRps.length();
            for (int i = 0; i < cont; i++) {
                chave.append(" ");
            }
        }

        //Tamanho 12, Preencher com zeros a esquerda
        if (numeroRps.toString().length() == 12) {
            chave.append(numeroRps);
        } else if (numeroRps.toString().length() > 12) {

        } else {
            int cont = 12 - numeroRps.toString().length();
            for (int i = 0; i < cont; i++) {
                chave.append("0");
            }
            chave.append(numeroRps);
        }

        //Tamanho 8
        if (dataEmissaoRPS.length() == 8) {
            chave.append(dataEmissaoRPS);
        } else if (dataEmissaoRPS.length() > 8) {
            //lança excessão
        }

        //Tamanho 2, Preencher com espaço a direita
        if (tributacao.length() == 2) {
            chave.append(tributacao);
        } else if (tributacao.length() > 2) {
            //lança excessão
        } else {
            chave.append(tributacao);
            int cont = 2 - tributacao.length();
            for (int i = 0; i < cont; i++) {
                chave.append(" ");
            }
        }

        //Tamanho 1
        if (situacaoRps.length() == 1) {
            chave.append(situacaoRps);
        } else if (situacaoRps.length() > 1) {
            //lança excessão
        }

        //Tamanho 1
        if (tipoRecolhimento.length() == 1) {
            chave.append(tipoRecolhimento);
        } else if (tipoRecolhimento.length() > 1) {
            //lança excessão
        }

        //Tamanho 15, Somente números, preencher com zeros a esquerda
        if (valorServico.toString().length() == 15) {
            chave.append(valorServico);
        } else if (valorServico.toString().length() > 15) {
            //lança excessão
        } else {
            int cont = 15 - valorServico.toString().length();
            for (int i = 0; i < cont; i++) {
                chave.append("0");
            }
            chave.append(valorServico);
        }

        //Tamanho 15, Somente números, preencher com zeros a esquerda
        if (valorDeducao.toString().length() == 15) {
            chave.append(valorDeducao);
        } else if (valorDeducao.toString().length() > 15) {
            //lança excessão
        } else {
            int cont = 15 - valorDeducao.toString().length();
            for (int i = 0; i < cont; i++) {
                chave.append("0");
            }
            chave.append(valorDeducao);
        }

        //Tamanho 10, Somente números, preencher com zeros a esquerda
        if (codigoAtividade.length() == 10) {
            chave.append(codigoAtividade);
        } else if (codigoAtividade.length() > 10) {
            //lança excessão
        } else {
            int cont = 10 - codigoAtividade.length();
            for (int i = 0; i < cont; i++) {
                chave.append("0");
            }
            chave.append(codigoAtividade);
        }

        //Tamanho 14, Somente números, preencher com zeros a esquerda
        if (cpfCnpjTomador.length() == 14) {
            chave.append(cpfCnpjTomador);
        } else if (cpfCnpjTomador.length() > 14) {
            //lança excessão
        } else {
            int cont = 14 - cpfCnpjTomador.length();
            for (int i = 0; i < cont; i++) {
                chave.append("0");
            }
            chave.append(cpfCnpjTomador);
        }
        return chave.toString();
    }

    public static Integer doubleEmInteiro(Double valor) {
        String toString = Double.toString(valor);
        String valorInteiro = toString.replace(".", "");
        return Integer.parseInt(valorInteiro);
    }

    /**
     * @author Assinatura Digital, adaptado Julianna Paz
     * @param arquivoParaAssinar
     * @param arquivoAssinado
     * @param tagName indica qual id será lido para validação
     * @param elementoID
     * @param tagSave indica qual hierarquia de tag o elemento vai se posicionar
     * @throws Exception
     */
    public static void gerarAssinaturaLote(String arquivoParaAssinar, String arquivoAssinado,
            String tagName, String elementoID, String tagSave) throws Exception {
        InputStream in = new FileInputStream(arquivoParaAssinar);
        OutputStream os = new FileOutputStream(arquivoAssinado);

        //elemento que deve ser assinado
//        String tagName = "Lote";
//        String elementoID = "lote:1ABCDZ";
        //chave(certificado)
        String alias = null;
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        org.w3c.dom.Document doc = dbf.newDocumentBuilder().parse(in);

        InputStream entrada = new FileInputStream(caminhoCertificado);
        KeyStore ks = KeyStore.getInstance("pkcs12");
        try {
            ks.load(entrada, senhaCertificado.toCharArray());
            alias = (String) ks.aliases().nextElement();
            if (ks.getEntry(alias, new KeyStore.PasswordProtection(senhaCertificado.toCharArray())) == null) {
                throw new Exception("Alias existe?");
            }
        } catch (IOException e) {
            throw new Exception("Senha do Certificado Digital incorreta ou Certificado inválido.");
        }

        KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) ks.getEntry(alias, new KeyStore.PasswordProtection(senhaCertificado.toCharArray()));

//        NodeList elements = doc.getElementsByTagName(tagName);
        Element el = (Element) doc.getElementsByTagName(tagName).item(0);
//        String idNota = el.getAttribute("Id");
        el.setIdAttribute("Id", true);

        DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), doc.getElementsByTagName(tagSave).item(0));

        //Assembling the XML Signature
        XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");

        List transforms = new ArrayList();
        transforms.add(fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null));
        transforms.add(fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null));

        Reference ref = fac.newReference("#" + elementoID, //
                fac.newDigestMethod(DigestMethod.SHA1, null),//
                transforms, null, null);

        SignedInfo si = fac.newSignedInfo(//
                fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, //
                        (C14NMethodParameterSpec) null), //
                fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null),//
                Collections.singletonList(ref));

        KeyInfoFactory kif = fac.getKeyInfoFactory();

        List x509Content = new ArrayList();
        x509Content.add(keyEntry.getCertificate());

        X509Data kv = kif.newX509Data(x509Content);
        KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));
        XMLSignature signature = fac.newXMLSignature(si, ki);

        signature.sign(dsc);

        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer trans = tf.newTransformer();

        //salva resultado no arquivo de saída
        trans.transform(new DOMSource(doc), new StreamResult(os));
    }

    public static String retirarCaracteresEspeciais(String texto) {
        CharSequence cs = new StringBuilder(texto);
        return Normalizer.normalize(cs, Normalizer.Form.NFKD).replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
    }

    /**
     * Usada para tratar o xml retornado do servidor
     */
    public static XmlHelper buscarBeanPorXml(String caminhoArquivo) {
        XStream xStream = null;
        FileReader ler = null;
        try {
            ler = new FileReader(caminhoArquivo);
            xStream = new XStream(new DomDriver()) {
                protected MapperWrapper wrapMapper(MapperWrapper next) {
                    return new MapperWrapper(next) {
                        public boolean shouldSerializeMember(Class definedIn, String fieldName) {
                            try {
                                return definedIn != Object.class || realClass(fieldName) != null;
                            } catch (CannotResolveClassException cnrce) {
                                return false;
                            }
                        }
                    };
                }
            };
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return new XmlHelper(xStream, ler);
    }
    
     public static double arredondar(double n, int casasDecimais) {
        //System.out.println("NAN "+ n);
        if (n == Double.NaN || Double.isNaN(n)) {
            n = 0;
        }
        BigDecimal bigDecimal = new BigDecimal(n);
        return bigDecimal.setScale(casasDecimais, BigDecimal.ROUND_HALF_UP).doubleValue();
    }
}
